package com.jinbooks.web.login.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.stp.parameter.SaLoginParameter;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jinbooks.authn.LoginCredential;
import com.jinbooks.authn.SignedPrincipal;
import com.jinbooks.authn.dto.LoginUser;
import com.jinbooks.authn.jwt.AuthJwt;
import com.jinbooks.authn.jwt.service.AuthRefreshTokenService;
import com.jinbooks.authn.session.Session;
import com.jinbooks.authn.session.SessionManager;
import com.jinbooks.authn.web.AuthorizationUtils;
import com.jinbooks.constants.ConstsStatus;
import com.jinbooks.entity.SocialsAssociate;
import com.jinbooks.entity.client.ClientUserAgent;
import com.jinbooks.entity.client.UserAgentParser;
import com.jinbooks.entity.history.HistoryLogin;
import com.jinbooks.entity.idm.UserInfo;
import com.jinbooks.exception.ServiceException;
import com.jinbooks.exception.user.UserException;
import com.jinbooks.persistence.mapper.SocialsAssociateMapper;
import com.jinbooks.persistence.mapper.UserInfoMapper;
import com.jinbooks.persistence.service.AuthzService;
import com.jinbooks.persistence.service.LoginService;
import com.jinbooks.web.WebConstants;
import com.jinbooks.web.WebContext;
import com.jinbooks.web.login.service.IAuthStrategy;
import com.jinbooks.web.login.service.SysLoginService;
import com.jinbooks.web.oauth2.social.properties.SocialProperties;
import com.jinbooks.web.oauth2.social.utils.SocialUtils;
import com.jinbooks.web.satoken.utils.LoginHelper;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 第三方授权策略
 *
 * @author thiszhc is 三三
 */
@Slf4j
@Service("social" + IAuthStrategy.BASE_NAME)
public class SocialAuthStrategy implements IAuthStrategy {

    private final SocialProperties socialProperties;
    private final UserInfoMapper userInfoMapper;
    private final SocialsAssociateMapper socialsAssociateMapper;
    private final SysLoginService loginService;
    private final LoginService baseLoginService;
    private final AuthzService authzService;
    private final AuthRefreshTokenService refreshTokenService;
    private final SessionManager sessionManager;

    public SocialAuthStrategy(SocialProperties socialProperties,
                              UserInfoMapper userInfoMapper,
                              SocialsAssociateMapper socialsAssociateMapper,
                              SysLoginService loginService,
                              LoginService baseLoginService,
                              AuthzService authzService,
                              AuthRefreshTokenService refreshTokenService,
                              SessionManager sessionManager) {
        this.socialProperties = socialProperties;
        this.userInfoMapper = userInfoMapper;
        this.socialsAssociateMapper = socialsAssociateMapper;
        this.loginService = loginService;
        this.baseLoginService = baseLoginService;
        this.authzService = authzService;
        this.refreshTokenService = refreshTokenService;
        this.sessionManager = sessionManager;
    }

    /**
     * 登录-第三方授权登录
     *
     * @param authentication     登录信息
     */
    @Override
    public AuthJwt login(Authentication authentication) {
        if (authentication == null) {
            return null;
        }

        LoginCredential credential = (LoginCredential) authentication;

        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
                credential.getProvider(), credential.getCode(),
                credential.getState(), socialProperties);
        if (!response.ok()) {
            throw new ServiceException(response.getMsg());
        }
        AuthUser authUserData = response.getData();

        String authId = authUserData.getSource() + authUserData.getUuid();
        List<SocialsAssociate> list = socialsAssociateMapper.selectList(new LambdaQueryWrapper<SocialsAssociate>().eq(SocialsAssociate::getAuthId, authId));
        if (CollUtil.isEmpty(list)) {
            throw new UserException("user.not.auth.bind");
        }
        SocialsAssociate social = list.get(0);

        UserInfo userInfo = loadUser(social.getUserId());
        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
        LoginUser loginUser = loginService.buildLoginUser(userInfo);

        SaLoginParameter model = new SaLoginParameter();
        // 生成token
        LoginHelper.login(loginUser, model);

        Authentication authResult = getAuthentication(userInfo);

        return new AuthJwt(
                StpUtil.getTokenValue(),
                authResult,
                StpUtil.getTokenTimeout());
    }

    public Authentication getAuthentication(UserInfo userInfo) {
        // 创建一个包含SignedPrincipal的Authentication对象
        SignedPrincipal signedPrincipal = new SignedPrincipal(userInfo);

        Authentication authResult = new AbstractAuthenticationToken(
                authzService.grantAuthority(userInfo)) {
            @Override
            public Object getCredentials() {
                return null;
            }

            @Override
            public Object getPrincipal() {
                return signedPrincipal;
            }
        };
        authResult.setAuthenticated(true);

        // 创建Session并存储认证信息
        Session session = new Session();
        session.setAuthentication(authResult);
        sessionManager.create(session.getId(), session);
        // 将认证信息存储到当前请求的Session中
        AuthorizationUtils.setAuthentication(authResult);

        // 创建SessionList记录并保存到数据库
        userInfo.setSessionId(session.getId());
        insertSessionList(session, userInfo);

        return authResult;
    }

    /**
     * 插入会话列表记录到数据库
     * @param session 会话对象
     * @param userInfo 用户信息
     */
    private void insertSessionList(Session session, UserInfo userInfo) {
        // 创建HistoryLogin对象用于记录登录历史和会话信息
        HistoryLogin historyLogin = new HistoryLogin();
        historyLogin.setSessionId(session.getId());
        historyLogin.setUserId(userInfo.getId());
        historyLogin.setUsername(userInfo.getUsername());
        historyLogin.setDisplayName(userInfo.getDisplayName());
        historyLogin.setBookId(userInfo.getWorkspaceId());
        historyLogin.setMessage(WebConstants.LOGIN_RESULT.SUCCESS);
        historyLogin.setLoginType("SOCIAL_SIGN_ON");

        // 设置客户端信息
        ClientUserAgent client = UserAgentParser.resolveUserAgent(WebContext.getRequest());
        historyLogin.setBrowser(client.getBrowser());
        historyLogin.setPlatform(client.getPlatform());
        String requestIpAddress = WebContext.getRequestIpAddress();
        historyLogin.setIpAddr(requestIpAddress);

        // 使用LoginService.insertHistory方法插入登录历史和会话信息
        baseLoginService.insertHistory(historyLogin);
    }

    private UserInfo loadUser(String userId) {
        UserInfo user = userInfoMapper.selectById(userId);
        if (ObjectUtil.isNull(user)) {
            throw new UserException("user.not.exists");
        } else if (user.getStatus() != ConstsStatus.ACTIVE) {
            throw new UserException("user.blocked");
        }
        return user;
    }

}